package la.bbs558.aop.delay;

import cn.hutool.core.thread.ThreadUtil;
import la.bbs558.exception.MsgException;
import la.bbs558.util.ExecutorManager;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Classname DelayManager
 * @Description TODO
 * @Date 2023/11/8 14:35
 * @Created by weihanmin
 */
@Slf4j
public class DelayQueueManager {

    private static final int PAUSE = 0;
    private static final int RUN = 1;
    private static final int DELETE = 2;
    private static final ExecutorService myExecutor = ExecutorManager.myExecutor;
    private static final Map<DelayQueueEnum, DelayQueueManager> delayManagerMap = new ConcurrentHashMap<>();
    public static DelayQueueManager getDelayQueue(DelayQueueEnum queueId) {
        return delayManagerMap.computeIfAbsent(queueId, DelayQueueManager::new);
    }

    @Getter
    private final DelayQueueEnum queueId;
    private final AtomicLong errorCount = new AtomicLong(0);
    private final AtomicInteger state = new AtomicInteger(RUN);
    private final DelayQueue<DelayTask<?>> delayQueue = new DelayQueue<>();

    DelayQueueManager(DelayQueueEnum queueId) {
        if (delayManagerMap.containsKey(queueId)) {
            throw new MsgException("已存在此延时任务{}", queueId);
        }
        this.queueId = queueId;
        myExecutor.execute(() -> {
            DelayQueueEnum curTaskId = this.getQueueId();
            DelayTask<?> delayTask = null;
            while (true) {
                try {
                    if (state.get() == DELETE) {
                        log.warn("延时队列「{}」已删除", curTaskId);
                        break;
                    }
                    if (state.get() == PAUSE) {
                        log.warn("延时队列「{}」已暂停", curTaskId);
                        ThreadUtil.sleep(500);
                        continue;
                    }
                    delayTask = delayQueue.poll();
                    if (delayTask == null) {
                        ThreadUtil.sleep(500);
                        continue;
                    }else if (delayTask.isFinish() || delayTask.isError()){
                        continue;
                    }
                    DelayTask<?> finalDelayTask = delayTask;
                    myExecutor.execute(() -> {
                        try {
                            finalDelayTask.exec(this);
                        } catch (Exception e) {
                            finalDelayTask.execError(this,e);
                        }
                    });
                } catch (Exception e) {
                    long count = errorCount.incrementAndGet();
                    log.error("延时队列「{}」异常,errorMsg={},异常次数={}", curTaskId, e.getMessage(), count);
                    if (delayTask != null) {
                        DelayHandler delayHandler = delayTask.getDelayHandler();
                        if (delayHandler != null) {
                            try {
                                delayTask.execError(this, e);
                                log.warn("延时队列「{}」执行errorHandler", curTaskId);
                            } catch (Exception ex) {
                                String msg = "延时队列「" + curTaskId + "」执行errorHandler异常";
                                log.error(msg, ex);
                            }
                        }
                    }
                    ThreadUtil.sleep(500);
                }
            }
            delayManagerMap.remove(curTaskId);
        });
        log.info("已创建新的延时队列，queueId={}", queueId);
    }

    private boolean checkDelete() {
        if (state.get() == DELETE) {
            delayManagerMap.remove(this.getQueueId());
            return true;
        }
        return false;
    }

    public boolean addTask(DelayTask<?> delayTask) {
        if (checkDelete()) return false;
        return delayQueue.add(delayTask);
    }

    public void pause() {
        state.set(PAUSE);
    }

    public void run() {
        state.set(RUN);
    }

    public void clear() {
        delayQueue.clear();
    }

    public void delete() {
        state.set(DELETE);
    }

}
